package pl.edu.pw.MidEmu.Integration;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Hashtable;

import pl.edu.pw.MidEmu.Integration.Common.Consts;

/**
 * Base class used for integration MidEmu with MIDAS enabled applications
 * Devcelopers have to call methods from this class to obtain informations
 * about virtual nodes required by MIDAS aplications
 * 
 * @author Mateusz Starodab
 *
 */
public class MidEmuApplication
{
	private static MidEmuApplication instance;

	/**
	 * Method used in singleton pattern to obtain instance of MidEmuApplication class
	 * 
	 * @return MidEmuApplication singleton instance
	 */
	public static MidEmuApplication getInstance()
	{
		if (instance == null)
			instance = new MidEmuApplication();
		return instance;
	}

	private String midasLocalNodeId;


	private String midasSensorServerIPAddress;


	private String midasSensorServerPort;

	/**
	 * Default constructor
	 */
	private MidEmuApplication()
	{

	}

	/**
	 * Returns node id of underlying MIDAS instance running on MidEMu virtual node
	 * @return MIDAS instance id
	 */
	public String getMidasLocalNodeId()
	{
		return midasLocalNodeId;
	}

	/**
	 * Gets MidEmu sensor server IP which is used in MidEmu -> MIDAS application communication
	 * @return Sensor Server IP
	 */
	public String getMidasSensorServerIPAddress()
	{
		return midasSensorServerIPAddress;
	}

	/**
	 * Gets MidEmu sensor server port which is used in MidEmu -> MIDAS application communication
	 * @return Sensor Server port
	 */
	public String getMidasSensorServerPort()
	{
		return midasSensorServerPort;
	}

	/**
	 * List all sensors running on underlying MidEmu node
	 * @return Hashtable where key is sensor id (guid) and value is sensor type
	 */
	public Hashtable<String, String> getSensors()
	{
		Hashtable<String, String> sensors = new Hashtable<String, String>();
		if (Integer.parseInt(midasSensorServerPort) > 0)
		{
			try
			{
				System.out.println("MidEmu integration component trying to connect to: " + midasSensorServerIPAddress + ":" + midasSensorServerPort);
				Socket clientSocket = new Socket();
				clientSocket.connect(new InetSocketAddress(midasSensorServerIPAddress, Integer.parseInt(midasSensorServerPort)));
				PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
				BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

				out.println("LIST");

				String fromServer = reader.readLine();

				if (fromServer != null)
				{
					String[] tokens = fromServer.split(";");
					for (int i = 0; i < tokens.length; i++)
					{
						if (tokens[i].equals("SENSOR"))
						{
							String id = tokens[i + 1];
							String type = tokens[i + 2];
							sensors.put(id, type);
							i = i + 2;
						}
					}
				}

				out.close();
				reader.close();
				clientSocket.close();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		return sensors;
	}

	/**
	 * Method used for MidEmuApplication initialization
	 * Should be called on MIDAS application startup
	 * When this method is not called other methods will not return any values
	 * @param args command line arguments passed to application
	 * @return returns 0 if success an 1 in case of initialzation failure (can't command line parameters)
	 */
	public int init(String[] args)
	{
		int startSuccess = 3;
		for (int i = 0; i < args.length; i++)
		{
			if (args[i].equals(Consts.MidasLocalNodeIdCommandLineParameterName))
			{
				midasLocalNodeId = args[i + 1];
				try
				{
					Integer.parseInt(midasLocalNodeId);
				}
				catch (NumberFormatException e)
				{
					return 1;
				}
				i++;
				startSuccess--;
			}
			if (args[i].equals(Consts.MidasSernsorServerIPAddress))
			{
				midasSensorServerIPAddress = args[i + 1];
				try
				{
					InetAddress.getByName(midasSensorServerIPAddress);
				}
				catch (UnknownHostException e)
				{
					return 1;
				}
				i++;
				startSuccess--;
			}
			if (args[i].equals(Consts.MidasSernsorServerPort))
			{
				midasSensorServerPort = args[i + 1];
				try
				{
					Integer.parseInt(midasSensorServerPort);
				}
				catch (NumberFormatException e)
				{
					return 1;
				}
				i++;
				startSuccess--;
			}
		}
		if (startSuccess == 0)
			return 0;
		return 1;
	}

	/**
	 * Method reads data provided by sensor
	 * @param id Id of the sensor
	 * @return Value of the sensor
	 */
	public String readSensor(String id)
	{
		StringBuilder readingString = new StringBuilder();
		if (Integer.parseInt(midasSensorServerPort) > 0)
		{
			try
			{
				Socket clientSocket = new Socket();
				clientSocket.connect(new InetSocketAddress(midasSensorServerIPAddress, Integer.parseInt(midasSensorServerPort)));
				PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
				BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

				out.println("READ;" + id + ";");

				String fromServer = reader.readLine();

				if (fromServer != null)
					readingString.append(fromServer);

				reader.close();
				clientSocket.close();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		return readingString.toString();
	}
}
